#include "stm32f4xx.h"
#include "board.h"
#include "console_it.h"
#include "debug.h"
#include "ad.h"
#include "dht11.h"
#include "led.h"
#include "OLED.h"
#include "console_timer.h"
#include <stdbool.h>
#include "voice_module.h"
#include "stm32f4xx_rtc.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#define         NO_USER_TICK            ( 2000 * 10 )
#define         RTC_WAKEUP_TIME         ( 20 ) // RTC定时唤醒时间，单位秒
#define         EXTI_NO_USER_TICK       pdMS_TO_TICKS(NO_USER_TICK) // 外部中断唤醒时无操作Tick数
#define         RTC_NO_USER_TICK        pdMS_TO_TICKS(NO_USER_TICK - (1000 * 15)) // RTC中断唤醒时无操作Tick数

// RTC时钟源初始化（只需执行一次，建议在main或系统初始化）
static void rtc_clock_init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RCC_LSEConfig(RCC_LSE_ON); // 启动LSE
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // 选择LSE作为RTC时钟
    RCC_RTCCLKCmd(ENABLE); // 使能RTC
}

uint32_t no_user_tick = pdMS_TO_TICKS(NO_USER_TICK); //初始无操作进入停止模式Tick数
uint32_t wfi_tick = 0; // 用于记录无操作时间
SemaphoreHandle_t rtc_wake_up_semaphore;
SemaphoreHandle_t exti_wake_up_semaphore;

static void rtc_wakeup_init(uint16_t seconds)
{
    // 每次STOP前都需重新配置RTC唤醒
    RTC_WakeUpCmd(DISABLE);
    RTC_ClearFlag(RTC_FLAG_WUTF);
    RTC_SetWakeUpCounter(seconds);
    RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
    RTC_ITConfig(RTC_IT_WUT, ENABLE);
    EXTI_ClearITPendingBit(EXTI_Line22);
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line22;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    RTC_WakeUpCmd(ENABLE);
}

static void ex_enter_stop_mode(void)
{
    // 关闭外设、设引脚为模拟输入等省电操作
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, DISABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, DISABLE);
    led_set_onoff(led2, false);
    led_set_onoff(led3, false);
    led_set_onoff(led4, false);
    led_set_onoff(led5, false);
    OLED_Clear();
    OLED_Update();
    // gpio_all_set_ain_config();   // 暂时不用
    EXTI_ClearITPendingBit(EXTI_Line5);
    SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
    // 进入STOP模式，等待RTC或外部中断唤醒
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
}

static void ex_exit_stop_mode(void)
{
    // 唤醒后恢复系统时钟
    SystemInit();
    SCB->VTOR = 0x08010000;     // 偏移中断向量表
    // 恢复外设
    console_it_init();
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    timer_init(100 - 1, 84 - 1);
    taskENTER_CRITICAL();
        OLED_Init();
    taskEXIT_CRITICAL();

    led_set_onoff(led5, true); // 点亮指示灯，指示退出停止模式

    #if DEBUG
        debug_init(115200);
    #endif
    voice_module_init();
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
    printf("exit stop mode\r\n");
}

void warning_buzzer_init(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_SetBits(GPIOE, GPIO_Pin_8);
}

void warning_buzzer_set_onoff(bool bit)
{
    if(bit)
    {
        GPIO_ResetBits(GPIOE, GPIO_Pin_8); // 蜂鸣器响
    }
    else
    {
        GPIO_SetBits(GPIOE, GPIO_Pin_8); // 蜂鸣器不响
    }
}

void wfi_task(void* param)
{
    rtc_clock_init();   // 初始化RTC时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    rtc_wake_up_semaphore = xSemaphoreCreateBinary();
    exti_wake_up_semaphore = xSemaphoreCreateBinary();
    rtc_wakeup_init(RTC_WAKEUP_TIME); // RTC定时唤醒
    console_it_init(); // 配置外部中断线PE5用于唤醒
    warning_buzzer_init();
    while (1)
    {
        wfi_tick = xTaskGetTickCount();

        if(xSemaphoreTake(wfi_wait_user_binary, no_user_tick) == pdTRUE)
        {
            #ifdef DEBUG
                // printf("User operation detected, resetting timer\n");
            #endif
            continue;
        }
        // 按键唤醒10秒无操作，进入STOP模式， RTC唤醒3秒无操作进入STOP模式
        if(xTaskGetTickCount() - wfi_tick >= no_user_tick)
        {
            #if DEBUG
                // printf("No user operation for %d seconds, entering STOP mode...\n",no_user_tick / 1000);
            #endif
            ex_enter_stop_mode(); // 进入STOP模式，等待RTC或外部中断唤醒
            ex_exit_stop_mode(); // 唤醒后恢复外设
        }
        // RTC唤醒：只采集和判断异常，不显示OLED，不恢复oled_task
        if(xSemaphoreTake(rtc_wake_up_semaphore, 0) == pdTRUE)
        {
            no_user_tick = RTC_NO_USER_TICK;

            taskENTER_CRITICAL();

            if(eTaskGetState(oled_task_handle) != eSuspended) // 防止嵌套挂起
                vTaskSuspend(oled_task_handle);

            uint16_t adc_val = adc_get_value();
            uint16_t mq2_val = mq2_get_value();
            dht_receive_data(WS_DHT);
            if(adc_val > 4000 || mq2_val < 140 || WS_DHT->temperature > 35 || WS_DHT->humidity > 100)
            {
                warning_buzzer_set_onoff(true);
                if(eTaskGetState(oled_task_handle) == eSuspended) // 数据异常点亮oled
                {
                    OLED_ShowString(72, 48, "mode: S", OLED_6X8);
                    vTaskResume(oled_task_handle);
                }
            }
            else
            {
                warning_buzzer_set_onoff(false);
            }

            taskEXIT_CRITICAL();

            RTC_SetWakeUpCounter(RTC_WAKEUP_TIME);
        }
        // 外部中断唤醒：采集、判断、恢复oled_task并显示信息
        else if(xSemaphoreTake(exti_wake_up_semaphore, 0) == pdTRUE)
        {
            no_user_tick = EXTI_NO_USER_TICK;
            uint16_t adc_val = adc_get_value();
            uint16_t mq2_val = mq2_get_value();
            dht_receive_data(WS_DHT);
            if(adc_val > 4000 || mq2_val < 140 || WS_DHT->temperature > 35 || WS_DHT->humidity > 100)
            {
                warning_buzzer_set_onoff(true);
            }
            else
            {
                warning_buzzer_set_onoff(false);
            }
            
            if(eTaskGetState(oled_task_handle) == eSuspended) // 防止嵌套解挂
            {
                OLED_ShowString(72, 48, "mode: S", OLED_6X8);
                vTaskResume(oled_task_handle);
            }
            
            RTC_SetWakeUpCounter(RTC_WAKEUP_TIME);
        }
    }
}

// 进入低功耗模式后由RTC定时中断唤醒
void RTC_WKUP_IRQHandler(void)
{
    if (RTC_GetITStatus(RTC_IT_WUT) != RESET)
    {
        RTC_ClearFlag(RTC_FLAG_WUTF);
        RTC_ClearITPendingBit(RTC_IT_WUT);
        EXTI_ClearITPendingBit(EXTI_Line22);
        BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
        xSemaphoreGiveFromISR(rtc_wake_up_semaphore, &pxHigherPriorityTaskWoken);
        if(pxHigherPriorityTaskWoken == pdTRUE)
        {
            portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
        }
    }
}

// 进入低功耗模式后由外部中断按键唤醒
void EXTI9_5_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line5) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line5);
        BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
        xSemaphoreGiveFromISR(exti_wake_up_semaphore, &pxHigherPriorityTaskWoken);
        if(pxHigherPriorityTaskWoken == pdTRUE)
        {
            portYIELD_FROM_ISR(pxHigherPriorityTaskWoken);
        }
    }
}

